﻿using System;
using System.Configuration;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentNHibernate.Mapping;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using ServiceStack.Logging;
using g42.Inc;
using g42.Authentication.NH;

namespace g42.Repos
{
  public static class db
  {
    public static NHibernate.ISessionFactory sessionFactory = null;
    static ILog log;

    public static ISessionFactory CreateSessionFactory(bool SchemaRebuild)
    {
      log = LogManager.GetLogger("db");
      try
      {
        string DatabaseType = ConfigurationManager.AppSettings["DatabaseType"];
        log.Info("DatabaseType = " + DatabaseType);
        if (DatabaseType == "PlatformDefault")
        {
          if (OSType.Get() == OSTypeEnum.Windows)
          {
            DatabaseType = ConfigurationManager.AppSettings["DatabaseTypeDefault_Windows"];
          }
          else
          {
            DatabaseType = ConfigurationManager.AppSettings["DatabaseTypeDefault_Linux"];
          }
        }
        log.Info("DatabaseType = " + DatabaseType);

        ISessionFactory sessinfac = null;
        FluentConfiguration fconf = null;
        switch (DatabaseType)
        {
          // SQL server
          case "MSQL": 
            fconf = Fluently.Configure()
              .Database(MsSqlConfiguration.MsSql2008
                .ConnectionString(c => c.FromAppSetting("DbConnection_MSQL"))
                .ShowSql())
              .Mappings(m => m.FluentMappings.AddFromAssemblyOf<g42.Repos.map>())
              .Mappings(m => m.FluentMappings.AddFromAssemblyOf<g42.Authentication.NH.UserAuthMap>());
            break;
         // FireBird on Linux (ok), aber nur mit FirebirdSql.Data.FirebirdClient.dll Version 2.5.2.0
          case "Firebird": 
            string fbConStr = ConfigurationManager.AppSettings["DBConnection_Firebird"];
            string[] fbconArr = fbConStr.Split(';');
            fbConStr = "";
            foreach (string VarVal in fbconArr)
            {
              // In der Config steht nur der Dateiname der FB-DB. Wir brauchen aber den kompletten Pfad
              // der ist immer ../fb/data relativ zur exe
              // In der config steht nicht der feste Pfad wegen Unterschied zwischen Unix/Windows
              string[] VarValArr = VarVal.Split ('=');
              if (VarValArr[0] == "Database")
              {
                string dbPath = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
                dbPath = System.IO.Directory.GetParent(dbPath).ToString();
                dbPath = Path.Combine(dbPath, "data", "fb");
                string dbFile = Path.GetFileName(VarValArr[1]);
                fbConStr = fbConStr + "Database=" + Path.Combine(dbPath, dbFile) + ";";
              }
              else
              {
                fbConStr = fbConStr + VarVal + ";";
              }
            }
            // 
            var fbConfLin = new FluentNHibernate.Cfg.Db.FirebirdConfiguration();
            fconf = Fluently.Configure()
              .Database(fbConfLin
                        .ConnectionString(fbConStr)
                        .ShowSql())
                .Mappings(m => m.FluentMappings.AddFromAssemblyOf<g42.Repos.map>())
                .Mappings(m => m.FluentMappings.AddFromAssemblyOf<g42.Authentication.NH.UserAuthMap>());
            break;
          // MySql auf Linux ok (die benötigte "mysql.data.dll" von oracle muss in "MySql.Data.dll" umbenannt werden)
          // ToDo: Test auf Windows
          case "MySql": 
            fconf = Fluently.Configure()
              .Database(FluentNHibernate.Cfg.Db.MySQLConfiguration.Standard
                        .ConnectionString(c => c.FromAppSetting("DBConnection_MySQL"))
                        .ShowSql())
                .Mappings(m => m.FluentMappings.AddFromAssemblyOf<g42.Repos.map>())
                .Mappings(m => m.FluentMappings.AddFromAssemblyOf<g42.Authentication.NH.UserAuthMap>());
            break;
          // SQLite 
          case "SQLite":
            string SQLiteDbFilename = "g42.db";
            string sqliteconf = ConfigurationManager.AppSettings["DBConnection_SQLite"];
            string[] sqliteconArr = sqliteconf.Split(';');
            foreach (string VarVal in sqliteconArr)
            {
              // In der Config steht nur der Dateiname der DB. Wir brauchen aber den kompletten Pfad
              // der ist immer ../SQLite/data relativ zur exe
              // In der config steht nicht der feste Pfad wegen Unterschied zwischen Unix/Windows
              string[] VarValArr = VarVal.Split ('=');
              if (VarValArr[0] == "Database")
              {
                string dbPath = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
                dbPath = System.IO.Directory.GetParent(dbPath).ToString();
                dbPath = Path.Combine(dbPath, "data", "SQLite");
                string dbFile = Path.GetFileName(VarValArr[1]);
                SQLiteDbFilename = Path.Combine(dbPath, dbFile);
                break;
              }
            }
            fconf = Fluently.Configure()
              .Database(FluentNHibernate.Cfg.Db.SQLiteConfiguration.Standard
                        .UsingFile(SQLiteDbFilename)
                        .ShowSql())
                .Mappings(m => m.FluentMappings.AddFromAssemblyOf<g42.Repos.map>())
                .Mappings(m => m.FluentMappings.AddFromAssemblyOf<g42.Authentication.NH.UserAuthMap>());
            break;
          default:
            throw new Exception("Databasetype not supported! " + DatabaseType);
        }

        if (fconf == null)
        {
          throw new Exception("Fatal Error: Could not create FluentConfiguration!!!");
        }

        if (SchemaRebuild)
        {
          sessinfac = fconf.ExposeConfiguration(BuildSchema).BuildSessionFactory();
        }
        else
        {
          sessinfac = fconf.BuildSessionFactory();
        }

        if (sessinfac == null)
        {
          throw new Exception("Fatal Error: Could not create ISessionFactory!!!");
        }

        return sessinfac;

      }
      catch (Exception E)
      {
        log.Fatal("Could not create session factory: " + E.Message + System.Environment.NewLine + E.InnerException);
        return null;
      }
    }

    public static void BuildSchema(NHibernate.Cfg.Configuration config)
    {
      // delete the existing db on each run
      // if (File.Exists(DbFile))        File.Delete(DbFile);

      // this NHibernate tool takes a configuration (with mapping info in)
      // and exports a database schema from it
      new SchemaExport(config)
        .Create(false, true);
    }
  }
}
